package com.hrg.rds.biz.robot.domain;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.hrg.rds.biz.config.RdsConfiguration;
import com.hrg.rds.biz.traffic.TrafficZoneVo;
import com.hrg.rds.biz.waybill.domain.instance.WaybillInstanceVo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Data
public class AgvVo {
    private String id;
    private String name;
    private String code;
    private AgvType type;// fork|roller|jack|hook
    private String ip;
    private String port;
    // 充电站点
    private String chargeStation;
    // 待命站点
    private String standbyStation;
    private Double radiusMax;

    // 可接单（占资源）、不接单占资源、不接单不占资源、已结单(不接单占资源)
    // 状态，重启时根据未结束的运单id进行恢复
    private volatile AgvStatus status;
    // 抢占控制权、释放控制权，根据配置是否不断 tryControl
    private volatile Boolean ifControlRights;
    private String currentControlRights;// object current_lock
    // 是否启用
    private volatile boolean ifEnabled;

    // 运单id，用于判断是否已接单，重启时根据未到达终态的运单id进行恢复
    private volatile String waybillInstanceId;

    @JsonIgnore
    private volatile WaybillInstanceVo waybillInstanceVo;
    //private boolean ifOnWaybillInstance = false;
    private volatile Date idleStartedTime = null;

    // 交管
    private Map<String, TrafficZoneVo> trafficZoneIdMap = new LinkedHashMap<>();
    // 当前导航点集合
    private List<String> navStations = new ArrayList<>();
    // 当前锁定点集合
    private List<String> lockStations = new ArrayList<>();

    // 消息接收时间，消息创建时间
    private Date msgReceivedTime;
    private Date msgCreateTime;//时间戳（2020-10-28T14:29:32.153Z）create_on
    // 连接状态
    private volatile boolean ifConnectActive;
    private volatile boolean ifConfigPushSuccess;
    private String currentStation;// string current_station 离机器人最近站点的 id（该判断比较严格，机器人必须很靠近某一个站点，这个距离可以通过参数配置中的 CurrentPointDist修改，默认为 0.3m。如果机器人与所有站点的距离大于这个距离，则这个字段为空字符。如果想要获取上一个完成任务的站点，可以取 finished_path 数组中的最后一个元素）
    private Double x;// number x 米 机器人的 x 坐标, 单位 m
    private Double y;// number y 米

    private Integer taskStatus;// number task_status 0 = NONE, 1 = WAITING(目前不可能出现该状态), 2 = RUNNING, 3 = SUSPENDED, 4 = COMPLETED, 5 = FAILED, 6 = CANCELED
    private Integer taskType;// number task_type 导航类型, 0 = 没有导航, 1 = 自由导航到任意点, 2 = 自由导航到站点, 3 = 路径导航到站点, 7 = 平动转动, 100 = 其他
    private String targetId;// string target_id 当前导航要去的站点, 仅当 task_type 为 2 或 3 时该字段有效, task_status 为 RUNNING 时说明正在去这个站点, task_status 为 COMPLETED 时说明已经到达这个站点, task_status 为 FAILED 时说明去这个站点失败, task_status 为 SUSPENDED 则说明去这个站点的导航暂停
    private List<Double> targetPoint;// array[number] target_point 当前导航要去的坐标点, 为一个包含三个元素的数组, 分别为在世界坐标系中的 x, y, r 坐标, 仅当 task_type 为 1 时该字段有效, task_status 为 RUNNING 时说明正在去这个坐标点, task_status 为 COMPLETED 时说明已经到达这个坐标点, task_status 为 FAILED 时说明去这个坐标点失败, task_status 为 SUSPENDED 则说明去这个坐标点的导航暂停
    private List<String> finishedPath;//array[string] 当前导航路径上已经经过的站点, 为站点的数组, 仅当 task_type 为 3 或 4 时该字段有效点。这里会列出所有已经经过的中间点
    private List<String> unfinishedPath;//当前导航路径上尚未经过的站点, 为站点的数组, 仅当 task_type 为 3 或 4 时该字段有效。这里会列出所有尚未经过的中间点

    // 地图名称
    private String usingMapName;//string current_map 当前地图名
    private String vehicleName;//string vehicle_id 机器人名称

    private Jack jack;// object jack 顶升相关状态，详见 1027
    private Fork fork;// object fork 货叉相关状态，详见 1028
    private Roller roller;// object roller 辊筒相关状态，详见 1029
    private Hook hook;// object hook 勾爪相关状态

    // 电池电量
    private Double batteryLevel;//number battery_level 机器人电池电量,范围 [0, 1]
    private Boolean ifCharging;//boolean charging 电池是否正在充电
    // 急停状态（包含软急停）
    private Boolean ifEmergencyStop;//boolean emergency、soft_emc
    private Boolean ifStop;//boolean is_stop 机器人底盘是否静止（行走电机）
    private Boolean ifBlocked;//boolean blocked 机器人是否被阻挡
    // 异常状态，如果是未接单时不可接单
    private Boolean ifError;//fatals/errors array[Object]
    // 异常信息
    private String errorMsg;
    private String trafficMsg;

    synchronized public void onWaybillInstanceFinished() {
        this.waybillInstanceVo = null;
        this.waybillInstanceId = null;
        idleStartedTime = new Date();
    }

    synchronized public void onWaybillInstanceRefreshed(WaybillInstanceVo waybillInstanceVo) {
        this.waybillInstanceVo = waybillInstanceVo;
        this.waybillInstanceId = waybillInstanceVo.getId();
        idleStartedTime = null;
    }

    synchronized public void onPushedMessage(String bodyStr) {
        PushedMessage message = JSONUtil.toBean(bodyStr, PushedMessage.class, true);
        this.msgReceivedTime = new Date();
        this.msgCreateTime = DateUtil.parse(message.getCreate_on());

        this.currentControlRights = message.getCurrent_lock() != null ? message.getCurrent_lock().getNick_name() : null;
        this.ifControlRights = RdsConfiguration.ControlRightsName.equals(this.currentControlRights);
        this.currentStation = message.getCurrent_station();
        this.x = message.getX();
        this.y = message.getY();
        this.taskStatus = message.getTask_status();
        this.taskType = message.getTask_type();
        this.targetId = message.getTarget_id();
        this.targetPoint = message.getTarget_point();
        this.finishedPath = message.getFinished_path();
        this.unfinishedPath = message.getUnfinished_path();
        this.usingMapName = message.getCurrent_map();
        this.vehicleName = message.getVehicle_id();
        this.jack = message.getJack();
        this.fork = message.getFork();
        this.roller = message.getRoller();
        this.hook = message.getHook();
        this.batteryLevel = 100*message.getBattery_level();
        this.ifCharging = message.getCharging();
        if (message.getEmergency() != null && message.getSoft_emc() != null) {
            this.ifEmergencyStop = message.getEmergency() || message.getSoft_emc();
        }
        if (message.getEmergency() == null) {
            this.ifEmergencyStop = message.getSoft_emc();
        }
        if (message.getSoft_emc() == null) {
            this.ifEmergencyStop = message.getEmergency();
        }
        this.ifStop = message.getIs_stop();
        this.ifBlocked = message.getBlocked();
        this.ifError = CollUtil.isNotEmpty(message.getFatals()) || CollUtil.isNotEmpty(message.getErrors());
        if (CollUtil.isNotEmpty(message.getFatals())) {
            this.errorMsg = message.getFatals().stream().map(Alert::getCode).collect(Collectors.joining(StrUtil.COMMA));
        } else if (CollUtil.isNotEmpty(message.getErrors())) {
            this.errorMsg = message.getErrors().stream().map(Alert::getCode).collect(Collectors.joining(StrUtil.COMMA));
        }
        // 消息超时
        /*if (DateUtil.between(this.msgCreateTime, this.msgReceivedTime, DateUnit.SECOND) > RdsConfiguration.PushedExpireSeconds) {
            RdsConfiguration.IfAgvReadyOnReconnect = false;
        } else */
        if (this.ifConnectActive) {
            RdsConfiguration.IfAgvReadyOnReconnect = true;
        }
    }

    public void onConnectActive(boolean rbkConnectOk) {
        log.error("onConnectActive = false");
        this.ifConnectActive = rbkConnectOk;
    }

    public void onConnectInactive(int port) {
        this.ifConnectActive = false;
        log.error("ifConnectActive = false");
        RdsConfiguration.IfAgvReadyOnReconnect = false;
        if (port == 19301) {
            this.ifConfigPushSuccess = false;
        }
    }

    // 系统重启时需要等待agv和运单状态初始化完成后才可以执行后续调度
    synchronized public boolean isIfAvailable() {
        //已启用、 控制权、可接单、无运单（或运单是自动运单）
        return ifEnabled && RdsConfiguration.IfWaybillReadyOnRestart && ifControlRights && status.equals(AgvStatus.KJD) && (StrUtil.isBlank(waybillInstanceId) || (StrUtil.isNotBlank(waybillInstanceId) && waybillInstanceVo.getWaybillTemplateId().equals("0"))) && isIfOk();
    }
    synchronized public boolean isIfAvailable4Auto() {
        //已启用、 控制权、可接单、无运单
        return ifEnabled && RdsConfiguration.IfWaybillReadyOnRestart && ifControlRights && status.equals(AgvStatus.KJD) && StrUtil.isBlank(waybillInstanceId) && isIfOk4ExcludeBatteryLevel();
    }

    synchronized public boolean isIfAvailable4Current() {
        return ifEnabled && RdsConfiguration.IfWaybillReadyOnRestart && ifControlRights && isIfOk4ExcludeBatteryLevel();
    }

    public boolean isIfOk() {
        return RdsConfiguration.IfAgvReadyOnReconnect && NumberUtil.compare(batteryLevel, 30) > 0 && !ifError && !ifEmergencyStop;
    }

    public boolean isIfOk4ExcludeBatteryLevel() {
        return RdsConfiguration.IfAgvReadyOnReconnect && !ifError && !ifEmergencyStop;
    }

    public boolean isStandByOrCharge() {
        return chargeStation.equals(currentStation) || standbyStation.equals(currentStation);
    }
}
